package com.mbiolance.cloud.activiti.service;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.mbiolance.cloud.activiti.domain.dto.ProcessInstanceDto;
import com.mbiolance.cloud.activiti.domain.vo.StartProcessVo;
import com.mbiolance.cloud.auth.common.SysUserInfoContext;
import com.mbiolance.cloud.auth.common.SystemRuntimeException;
import com.mbiolance.cloud.auth.domain.PageModel;
import com.mbiolance.cloud.auth.domain.dto.SysUserDto;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.javax.el.ExpressionFactory;
import org.activiti.engine.impl.javax.el.ValueExpression;
import org.activiti.engine.impl.juel.ExpressionFactoryImpl;
import org.activiti.engine.impl.juel.SimpleContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProcessInstanceService {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;
    
    @Autowired
    private TaskService taskService;

    public void completeTask(String id, Map<String, Object> variables) {
        Task task = taskService.createTaskQuery().processInstanceId(id).singleResult();
        if (variables == null) {
            variables = new HashMap<>();
        }
        try {
            taskService.complete(task.getId(), variables);
        } catch (Exception e) {
            throw new SystemRuntimeException(e.getMessage());

        }
    }

    /**
     * 任务拾取，基于processInstanceId
     */
    public Boolean claim(String id) {
        Task task = taskService.createTaskQuery().processInstanceId(id).singleResult();
        SysUserDto user = SysUserInfoContext.getUser();
        taskService.claim(task.getId(), String.valueOf(user.getId()));
        return true;
    }

    public Boolean retTask(String id) {
        Task task = taskService.createTaskQuery().processInstanceId(id).singleResult();
        taskService.setAssignee(task.getId(), null);
        return true;
    }

    public List<ProcessInstanceDto> listByIds(List<String> processInstanceIds) {
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
        List<ProcessInstance> list = processInstanceQuery.processInstanceIds(new HashSet<>(processInstanceIds)).list();
        List<ProcessInstanceDto> dtos = new ArrayList<>();
        if (CollectionUtil.isEmpty(list)) {
            return dtos;
        }
        TaskQuery taskQuery = taskService.createTaskQuery().processInstanceIdIn(processInstanceIds);
        List<Task> taskList = taskQuery.list();
        Map<String, Task> taskMap = taskList.stream().collect(Collectors.toMap(Task::getProcessInstanceId, v -> v));
        for (ProcessInstance instance : list) {
            Task task = Optional.ofNullable(taskMap.get(instance.getId())).orElse(new TaskEntity());
            dtos.add(ProcessInstanceDto.builder()
                    .processDefinitionId(instance.getProcessDefinitionId()).processDefinitionVersion(instance.getProcessDefinitionVersion())
                    .deploymentId(instance.getDeploymentId()).businessKey(instance.getBusinessKey()).description(instance.getDescription())
                    .processDefinitionName(instance.getProcessDefinitionName()).processDefinitionKey(instance.getProcessDefinitionKey())
                    .name(instance.getName()).id(instance.getId())
                    .taskId(task.getId()).taskName(task.getName())
                    .build());
        }
        return dtos;
    }

    public PageModel<ProcessInstanceDto> page(Integer number, Integer size) {
        if (number <= 0) {
            number = 1;
        }
        number -= 1;
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
        List<ProcessInstance> processInstances = processInstanceQuery.orderByProcessInstanceId().desc().listPage(number * size, size);

        List<Deployment> deployments = repositoryService.createDeploymentQuery().list();
        Map<String, String> deploymentMap = deployments.stream().collect(Collectors.toMap(Deployment::getId, Deployment::getName));
        long count = processInstanceQuery.count();
        List<ProcessInstanceDto> dtos = new ArrayList<>();
        List<String> processInstanceIds = processInstances.stream().map(ProcessInstance::getId).collect(Collectors.toList());
        Map<String, Task> taskMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(processInstanceIds)) {
            TaskQuery taskQuery = taskService.createTaskQuery().processInstanceIdIn(processInstanceIds);
            List<Task> taskList = taskQuery.list();
            taskMap = taskList.stream().collect(Collectors.toMap(Task::getProcessInstanceId, v -> v));
        }

        for (ProcessInstance instance : processInstances) {
            Task task = Optional.ofNullable(taskMap.get(instance.getId())).orElse(new TaskEntity());
            dtos.add(ProcessInstanceDto.builder()
                    .processDefinitionId(instance.getProcessDefinitionId()).processDefinitionVersion(instance.getProcessDefinitionVersion())
                    .deploymentId(instance.getDeploymentId()).businessKey(instance.getBusinessKey()).description(instance.getDescription())
                    .processDefinitionName(instance.getProcessDefinitionName()).processDefinitionKey(instance.getProcessDefinitionKey())
                    .name(instance.getName()).id(instance.getId())
                    .deployName(deploymentMap.get(instance.getDeploymentId()))
                    .taskId(task.getId()).taskName(task.getName())
                    .build());
        }
        return PageModel.<ProcessInstanceDto>builder()
                .count((int) count).number(number + 1).size(size)
                .content(dtos)
                .build();
    }

    /**
     * 启动流程
     * @param vo    流程启动参数
     * @return      已启动的流程实例的id
     */
    public String start(StartProcessVo vo) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(vo.getProcessDefinitionId()
                , vo.getBusinessKey()
                , vo.getParams());
        return processInstance.getId();
    }

    /**
     * 根据流程id完成任务
     * @param id            流程实例id
     * @param variables     任务参数
     */
    public void nextTasks(String id, Map<String, Object> variables) {
        Task task = taskService.createTaskQuery().processInstanceId(id).singleResult();
        try {
            taskService.complete(task.getId(), variables);
        } catch (Exception e) {
            throw new SystemRuntimeException(e.getMessage());
        }
    }

    /**
     * 历史流程实例列表
     */
    public PageModel<ProcessInstanceDto> historyPage(Integer number, Integer size) {
        if (number <= 0) {
            number = 1;
        }
        number -= 1;
        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();
        List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery.finished().listPage(number * size, size);

        long count = historicProcessInstanceQuery.count();
        List<ProcessInstanceDto> dtos = new ArrayList<>();
        for (HistoricProcessInstance instance : historicProcessInstances) {
            dtos.add(ProcessInstanceDto.builder()
                    .processDefinitionId(instance.getProcessDefinitionId()).processDefinitionVersion(instance.getProcessDefinitionVersion())
                    .deploymentId(instance.getDeploymentId()).businessKey(instance.getBusinessKey()).description(instance.getDescription())
                    .processDefinitionName(instance.getProcessDefinitionName()).processDefinitionKey(instance.getProcessDefinitionKey())
                    .name(instance.getName()).id(instance.getId())
                    .startTime(instance.getStartTime()).endTime(instance.getEndTime())
                    .build());
        }
        return PageModel.<ProcessInstanceDto>builder()
                .count((int) count).number(number + 1).size(size)
                .content(dtos)
                .build();
    }

    /**
     *  历史活动查询
     */
    public void historyActInstanceList(String processInstanceId) {
        List<HistoricActivityInstance>  list = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .finished().list();
        for(HistoricActivityInstance hai:list){
            System.out.println("活动ID:"+hai.getId());
            System.out.println("流程实例ID:"+hai.getProcessInstanceId());
            System.out.println("活动名称："+hai.getActivityName());
            System.out.println("办理人："+hai.getAssignee());
            System.out.println("开始时间："+hai.getStartTime());
            System.out.println("结束时间："+hai.getEndTime());
            System.out.println("=================================");
        }
    }

    /**
     *  历史任务查询
     */
    public void historyTaskList(String processInstanceId) {
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId).finished().list();
        for(HistoricTaskInstance hti:list){
            System.out.println("任务ID:"+hti.getId());
            System.out.println("流程实例ID:"+hti.getProcessInstanceId());
            System.out.println("任务名称："+hti.getName());
            System.out.println("办理人："+hti.getAssignee());
            System.out.println("开始时间："+hti.getStartTime());
            System.out.println("结束时间："+hti.getEndTime());
            System.out.println("=================================");
        }}


    /**
     * 获取下一个用户任务信息
     * @return  下一个用户任务用户组信息
     */
    public TaskDefinition getNextTaskInfo(String processInstanceId) {
        ProcessDefinitionEntity processDefinitionEntity = null;
        String id = null;
        TaskDefinition task = null;
        //获取流程实例Id信息
        //String processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
        //获取流程发布Id信息
        String definitionId = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult().getProcessDefinitionId();
        processDefinitionEntity = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
                .getDeployedProcessDefinition(definitionId);
        ExecutionEntity execution = (ExecutionEntity) runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        //当前流程节点Id信息
        String activitiId = execution.getActivityId();
        //获取流程所有节点信息
        List<ActivityImpl> activitiList = processDefinitionEntity.getActivities();
        //遍历所有节点信息
        for (ActivityImpl activityImpl : activitiList) {
            id = activityImpl.getId();
            if (activitiId.equals(id)) {
                //获取下一个节点信息
                task = nextTaskDefinition(activityImpl, activityImpl.getId(), null, processInstanceId);
                break;
            }
        }
        return task;
    }

    /**
     * 下一个任务节点信息,
     * 如果下一个节点为用户任务则直接返回,
     *
     * 如果下一个节点为排他网关, 获取排他网关Id信息, 根据排他网关Id信息和execution获取流程实例排他网关Id为key的变量值,
     * 根据变量值分别执行排他网关后线路中的el表达式, 并找到el表达式通过的线路后的用户任务
     * @param activityImpl     流程节点信息
     * @param activityId             当前流程节点Id信息
     * @param elString               排他网关顺序流线段判断条件
     * @param processInstanceId      流程实例Id信息
     * @return
     */
    private TaskDefinition nextTaskDefinition(ActivityImpl activityImpl, String activityId, String elString, String processInstanceId){
        PvmActivity ac = null;
        Object s = null;
        // 如果遍历节点为用户任务并且节点不是当前节点信息
        if ("userTask".equals(activityImpl.getProperty("type")) && !activityId.equals(activityImpl.getId())) {
            // 获取该节点下一个节点信息
            TaskDefinition taskDefinition = ((UserTaskActivityBehavior) activityImpl.getActivityBehavior())
                    .getTaskDefinition();
            return taskDefinition;
        } else if("exclusiveGateway".equals(activityImpl.getProperty("type"))){
            // 当前节点为exclusiveGateway
            List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();
            //outTransitionsTemp = ac.getOutgoingTransitions();
            // 如果网关路线判断条件为空信息
//          if (StringUtils.isEmpty(elString)) {
            // 获取流程启动时设置的网关判断条件信息
            elString = getGatewayCondition(activityImpl.getId(), processInstanceId);
//          }
            // 如果排他网关只有一条线路信息
            if (outTransitions.size() == 1) {
                return nextTaskDefinition((ActivityImpl) outTransitions.get(0).getDestination(), activityId,
                        elString, processInstanceId);
            } else if (outTransitions.size() > 1) { // 如果排他网关有多条线路信息
                for (PvmTransition tr1 : outTransitions) {
                    s = tr1.getProperty("conditionText"); // 获取排他网关线路判断条件信息
                    // 判断el表达式是否成立
                    if (isCondition(activityImpl.getId(), StringUtils.trim(s.toString()), elString)) {
                        return nextTaskDefinition((ActivityImpl) tr1.getDestination(), activityId, elString,
                                processInstanceId);
                    }
                }
            }
        } else {
            // 获取节点所有流向线路信息
            List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();
            List<PvmTransition> outTransitionsTemp = null;
            for (PvmTransition tr : outTransitions) {
                ac = tr.getDestination(); // 获取线路的终点节点
                // 如果流向线路为排他网关
                if ("exclusiveGateway".equals(ac.getProperty("type"))) {
                    outTransitionsTemp = ac.getOutgoingTransitions();
                    // 如果网关路线判断条件为空信息
                    if (StringUtils.isEmpty(elString)) {
                        // 获取流程启动时设置的网关判断条件信息
                        elString = getGatewayCondition(ac.getId(), processInstanceId);
                    }
                    // 如果排他网关只有一条线路信息
                    if (outTransitionsTemp.size() == 1) {
                        return nextTaskDefinition((ActivityImpl) outTransitionsTemp.get(0).getDestination(), activityId,
                                elString, processInstanceId);
                    } else if (outTransitionsTemp.size() > 1) { // 如果排他网关有多条线路信息
                        for (PvmTransition tr1 : outTransitionsTemp) {
                            s = tr1.getProperty("conditionText"); // 获取排他网关线路判断条件信息
                            // 判断el表达式是否成立
                            if (isCondition(ac.getId(), StringUtils.trim(s.toString()), elString)) {
                                return nextTaskDefinition((ActivityImpl) tr1.getDestination(), activityId, elString,
                                        processInstanceId);
                            }
                        }
                    }
                } else if ("userTask".equals(ac.getProperty("type"))) {
                    return ((UserTaskActivityBehavior) ((ActivityImpl) ac).getActivityBehavior()).getTaskDefinition();
                } else {
                }
            }
            return null;
        }
        return null;
    }

    /**
     * 查询流程启动时设置排他网关判断条件信息
     * @param gatewayId          排他网关Id信息, 流程启动时设置网关路线判断条件key为网关Id信息
     * @param processInstanceId  流程实例Id信息
     * @return
     */
    public String getGatewayCondition(String gatewayId, String processInstanceId) {
        Execution execution = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).singleResult();
        Object object= runtimeService.getVariable(execution.getId(), gatewayId);
        return object==null? "":object.toString();
    }

    /**
     * 根据key和value判断el表达式是否通过信息
     * @param key    el表达式key信息
     * @param el     el表达式信息
     * @param value  el表达式传入值信息
     * @return
     */
    public boolean isCondition(String key, String el, String value) {
        ExpressionFactory factory = new ExpressionFactoryImpl();
        SimpleContext context = new SimpleContext();
        context.setVariable(key, factory.createValueExpression(value, String.class));
        ValueExpression e = factory.createValueExpression(context, el, boolean.class);
        return (Boolean) e.getValue(context);
    }

}
